/*
 * Copyright (c) 2022, IMMORTA Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * - Neither the name of IMMORTA Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*!
 * @file wdg_reg_access.c
 * @brief This file defines wdg register access functions
 */

/*******Includes***************************************************************/
#include "wdg_reg_access.h"

/*******Definitions************************************************************/

/*******Variables**************************************************************/

/*******Prototypes*************************************************************/

/*******Private Functions******************************************************/

/*******Code*******************************************************************/

/*******************************************************************************
 *** Function Name : WDG_REG_Init
 *** Description   : WDG init
 ******************************************************************************/
errcode_t WDG_REG_Init(WDG_Type* regBase, const wdg_config_t* wdgConfig)
{
    volatile uint32_t mask;
    errcode_t errCode = ERR_SUCCESS;
    bool enabled = WDG_REG_IsEnabled(regBase);
    bool updateEnabled = WDG_REG_IsUpdateEnable(regBase);
    uint32_t cs = regBase->CS;

    mask = System_SuspendAllInterrupts();

    if (!enabled && updateEnabled) {
        cs &= ~(WDG_CS_WIN_Msk | WDG_CS_PRES_Msk | WDG_CS_CLK_Msk | WDG_CS_INT_Msk | WDG_CS_UPDATE_Msk);

        cs |= WDG_CS_CLK(wdgConfig->clockSource);
        cs |= WDG_CS_UPDATE(wdgConfig->updateEnable ? 1UL : 0UL);
        cs |= WDG_CS_INT(wdgConfig->intEnable ? 1UL : 0UL);
        cs |= WDG_CS_PRES(wdgConfig->prescalerEnable ? 1UL : 0UL);
        cs |= WDG_CS_WIN(wdgConfig->windowModeEnable ? 1UL : 0UL);

        cs |= WDG_CS_EN_Msk;

        WDG_UNLOCK(regBase);

        while (!WDG_IsUnlocked(regBase)) {

        }
        regBase->TOVAL = wdgConfig->timeoutValue;

        if (wdgConfig->windowModeEnable) {
            regBase->WIN = wdgConfig->windowValue;
        }

        regBase->CS = cs;

        while (WDG_IsUnlocked(regBase)) {

        }

        while (!WDG_REG_IsReconfigurationComplete(regBase)) {
            /* Wait until the configuration is complete */
        }

    } else {
        errCode = ERR_ERROR;
    }

    System_ResumeAllInterrupts(mask);

    return errCode;
}

/*******************************************************************************
 *** Function Name : WDG_REG_SetIntEnable
 *** Description   : Set WDG interrupt enalbe/disable
 ******************************************************************************/
void WDG_REG_SetIntEnable(WDG_Type* regBase, bool enable)
{
    WDG_UNLOCK(regBase);

    regBase->CS = (regBase->CS & ~WDG_CS_INT_Msk) | WDG_CS_INT(enable ? 1UL : 0UL);

    while (!WDG_REG_IsReconfigurationComplete(regBase)) {
        /* Wait until the configuration is complete */
    }
}

/*******************************************************************************
 *** Function Name : WDG_REG_Deinit
 *** Description   : wdg deinit
 ******************************************************************************/
void WDG_REG_Deinit(WDG_Type* regBase)
{
    volatile uint32_t mask;

    mask = System_SuspendAllInterrupts();

    WDG_UNLOCK(regBase);

    regBase->CS = WDG_CS_RESET_VALUE & (~WDG_CS_EN_Msk);
    regBase->TOVAL = WDG_TO_RESET_VALUE;
    regBase->WIN = WDG_WIN_RESET_VALUE;

    while (!WDG_REG_IsReconfigurationComplete(regBase)) {
        /* Wait until the configuration is complete */
    }

    System_ResumeAllInterrupts(mask);
}

/*******************************************************************************
 *** Function Name : WDG_REG_GetConfig
 *** Description   : Get WDG config
 ******************************************************************************/
wdg_config_t WDG_REG_GetConfig(const WDG_Type* regBase)
{
    wdg_config_t config;

    uint32_t cs = regBase->CS;

    config.updateEnable     = ((cs & WDG_CS_UPDATE_Msk) != 0U);
    config.intEnable        = ((cs & WDG_CS_INT_Msk) != 0U);
    config.windowModeEnable = ((cs & WDG_CS_WIN_Msk) != 0U);
    config.prescalerEnable  = ((cs & WDG_CS_PRES_Msk) != 0U);
    config.windowValue      = (uint16_t)regBase->WIN;
    config.timeoutValue     = (uint16_t)regBase->TOVAL;

    switch ((cs & WDG_CS_CLK_Msk) >> WDG_CS_CLK_Pos) {
        case 0U:
#if (CONFIG_WDG_SUPPORT_FUNC_CLK_HSI == 0U)
            config.clockSource = WDG_CLK_SRC_APB;
#elif (CONFIG_WDG_SUPPORT_FUNC_CLK_HSI == 1U)
            config.clockSource = WDG_CLK_SRC_HSI;
#endif
            break;

        case 1U:
            config.clockSource = WDG_CLK_SRC_LSI;
            break;

        default:
#if (CONFIG_WDG_SUPPORT_FUNC_CLK_HSI == 0U)
            config.clockSource = WDG_CLK_SRC_APB;
#elif (CONFIG_WDG_SUPPORT_FUNC_CLK_HSI == 1U)
            config.clockSource = WDG_CLK_SRC_HSI;
#endif
            break;
    }

    return config;
}

/*******EOF********************************************************************/
